package AnalisadorSintatico;

import AnalisadorLexico.ExpressaoRegular;
import AnalisadorLexico.Token;
import Principal.Erro;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

/**
 *
 * @author Fabio Novais
 */
public final class Analisador
{

    private File _arquivoTemporario;
    private FileReader _fr;
    private BufferedReader _bf;
    private FileWriter _fw;
    private Object _bw;
    private int _linhaAtual = 0;
    private ArrayList<Erro> _listaErrosSintaticos;

    public Analisador() throws IOException
    {
        IniciarArquivo();
        _listaErrosSintaticos = new ArrayList<Erro>();
    }

    public ArrayList<Erro> getListaErros()
    {
        return this._listaErrosSintaticos;
    }

    public void Analisar() throws IOException
    {
        ConverterArquivoTemporarioListaTokens();
        FecharArquivoTemporario();

        BufferedReader bf = AbrirArquivo("doCommentSintatico.dom");

        String linha = "";

        _linhaAtual = 1;
        String linhaCodigoDoComment = "";
        while ((linha = bf.readLine()) != null)
        {
            _linhaAtual = Integer.parseInt(linha.split("%%")[0]);
            String[] arrayTokens;

            linhaCodigoDoComment = linha.split("%%")[1].trim();

            if (linhaCodigoDoComment.startsWith("#"))
            {
                arrayTokens = linha.split("%%")[1].split("#")[1].trim().split(" ");

                ReconhecerTDDESCRIPTION(arrayTokens, _linhaAtual);
                ReconhecerTDAUTHOR(arrayTokens, _linhaAtual);
                ReconhecerTDSINCE(arrayTokens, _linhaAtual);
                ReconhecerTDVERSION(arrayTokens, _linhaAtual);
                ReconhecerTDPARAM(arrayTokens, _linhaAtual);
                ReconhecerTDSEE(arrayTokens, _linhaAtual);
                ReconhecerTDDEPRECATED(arrayTokens, _linhaAtual);
                ReconhecerTDRETURN(arrayTokens, _linhaAtual);
                ReconhecerTTFIXME(arrayTokens, _linhaAtual);
                ReconhecerTTTODO(arrayTokens, _linhaAtual);
            } else
            {
                arrayTokens = linhaCodigoDoComment.split(" ");

                ReconhecerTDEFCLASS(arrayTokens, _linhaAtual);
                ReconhecerTDEFMETHOD(arrayTokens, _linhaAtual);
            }
        }
        ImprimirListaErros();
    }

    private void ReconhecerTDDESCRIPTION(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTDDESCRIPTION(), "").trim().equals(""))
        {
            if (arrayTokens.length > 2)
            {
                String mensagemErro = "";

                for (int i = 2; i < arrayTokens.length; i++)
                {
                    mensagemErro += arrayTokens[i] + " ";
                }

                Erro erro = new Erro(2, linhaAtual, "Final de linha esperado mas \"" + mensagemErro.trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);

                return;
            }
            if (arrayTokens.length < 2)
            {
                Erro erro = new Erro(2, linhaAtual, "Descrição esperada mas final de linha encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
            if (!arrayTokens[1].replaceAll(Token.getTSTRING(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Descrição esperada, mas \"" + arrayTokens[1].trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);


            }
        }
    }

    private void ReconhecerTDAUTHOR(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTDAUTHOR(), "").trim().equals(""))
        {
            if (arrayTokens.length <= 1)
            {
                Erro erro = new Erro(2, linhaAtual, "Nome do autor esperado.");
                _listaErrosSintaticos.add(erro);

                return;
            }
            if (!arrayTokens[1].replaceAll(",", "").replaceAll(Token.getTSTRING(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Nome do autor esperado.");
                _listaErrosSintaticos.add(erro);

                return;
            }
            for (int i = 1; i < arrayTokens.length; i++)
            {
                if (arrayTokens[i].replaceAll(",", "").replaceAll(Token.getTSTRING(), "").equals(""))
                {
                    if (arrayTokens[i].startsWith(",") || arrayTokens[i].endsWith(","))
                    {
                        Erro erro = new Erro(2, linhaAtual, "Caractere de separação em lugar não válido.");
                        _listaErrosSintaticos.add(erro);

                        return;
                    }
                    if (i + 1 >= arrayTokens.length)
                    {
                        Erro erro = new Erro(2, linhaAtual, "Email esperado.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }
                    if (!arrayTokens[i + 1].replace(",", "").replaceAll(ExpressaoRegular.getTEMAIL(), "").equals(""))
                    {
                        Erro erro = new Erro(2, linhaAtual, "Email esperado.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }

                }
                if (arrayTokens[i].replaceAll(",", "").replaceAll(ExpressaoRegular.getTEMAIL(), "").equals(""))
                {
                    if (!arrayTokens[i - 1].replaceAll(",", "").replaceAll(Token.getTSTRING(), "").equals(""))
                    {
                        Erro erro = new Erro(2, linhaAtual, "Nome do autor deve vir antes do endereço de email.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }
                    if (i + 1 < arrayTokens.length)
                    {
                        if (!arrayTokens[i].endsWith(","))
                        {
                            Erro erro = new Erro(2, linhaAtual, "Caractere de separação faltando ou inválido");
                            _listaErrosSintaticos.add(erro);
                            return;
                        }
                        if (!arrayTokens[i + 1].replace(",", "").replaceAll(Token.getTSTRING(), "").equals(""))
                        {
                            Erro erro = new Erro(2, linhaAtual, "Nome do autor esperado.");
                            _listaErrosSintaticos.add(erro);
                            return;
                        }
                    } else
                    {
                        if (arrayTokens[i].endsWith(","))
                        {
                            Erro erro = new Erro(2, linhaAtual, "Caractere de separação em lugar não válido.");
                            _listaErrosSintaticos.add(erro);
                            return;
                        }
                    }
                }
            }

        }

    }

    private void ReconhecerTDSINCE(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTDSINCE(), "").trim().equals(""))
        {
            if (arrayTokens.length > 2)
            {
                String mensagemErro = "";

                for (int i = 2; i < arrayTokens.length; i++)
                {
                    mensagemErro += arrayTokens[i] + " ";
                }

                Erro erro = new Erro(2, linhaAtual, "Final de linha esperado mas \"" + mensagemErro.trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);

                return;
            }

            if (arrayTokens.length < 2)
            {
                Erro erro = new Erro(2, linhaAtual, "Data esperada mas final de linha encontrado.");
                _listaErrosSintaticos.add(erro);

                return;
            }
            if (!arrayTokens[1].replaceAll(ExpressaoRegular.getTDATE(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Data esperado mas \"" + arrayTokens[1].trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);

                return;
            }
        }
    }

    private void ReconhecerTDVERSION(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTDVERSION(), "").equals(""))
        {
            if (arrayTokens.length > 2)
            {
                String mensagemErro = "";

                for (int i = 2; i
                        < arrayTokens.length; i++)
                {
                    mensagemErro += arrayTokens[i] + " ";
                }

                Erro erro = new Erro(2, linhaAtual, "Final de linha esperado mas \"" + mensagemErro.trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);

                return;
            }

            if (arrayTokens.length < 2)
            {
                Erro erro = new Erro(2, linhaAtual, "Data esperada mas final de linha encontrado.");
                _listaErrosSintaticos.add(erro);

                return;
            }
            if (!arrayTokens[1].replaceAll(ExpressaoRegular.getTVERSION(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Número da versão esperado mas \"" + arrayTokens[1].trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);

                return;
            }

        }
    }

    private void ReconhecerTDPARAM(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTDPARAM(), "").trim().equals(""))
        {
            if (arrayTokens.length <= 3)
            {
                Erro erro = new Erro(2, linhaAtual, "Número de argumentos inválido.");
                _listaErrosSintaticos.add(erro);

                return;
            }
            if (!arrayTokens[1].replaceAll(",", "").replaceAll(ExpressaoRegular.getTPARAM(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Parâmetro esperado.");
                _listaErrosSintaticos.add(erro);

                return;
            }

            if (!arrayTokens[2].replaceAll(",", "").replaceAll(ExpressaoRegular.getTDATATYPE(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Descrição do parâmetro esperado.");
                _listaErrosSintaticos.add(erro);

                return;
            }
            if (!arrayTokens[3].replaceAll(",", "").replaceAll(Token.getTSTRING(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Descrição do parâmetro esperado.");
                _listaErrosSintaticos.add(erro);

                return;
            }

            for (int i = 1; i < arrayTokens.length; i++)
            {
                if (arrayTokens[i].replaceAll(",", "").replaceAll(ExpressaoRegular.getTPARAM(), "").equals(""))
                {
                    if (arrayTokens[i].startsWith(",") || arrayTokens[i].endsWith(","))
                    {
                        Erro erro = new Erro(2, linhaAtual, "Caractere de separação em lugar não válido.");
                        _listaErrosSintaticos.add(erro);

                        return;
                    }
                    if (i + 1 >= arrayTokens.length)
                    {
                        Erro erro = new Erro(2, linhaAtual, "Tipo do parâmetro esperado.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }
                    if (!arrayTokens[i + 1].replace(",", "").replaceAll(ExpressaoRegular.getTDATATYPE(), "").equals(""))
                    {
                        Erro erro = new Erro(2, linhaAtual, "Tipo do parâmetro esperado.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }
                    if (i + 2 >= arrayTokens.length)
                    {
                        Erro erro = new Erro(2, linhaAtual, "Descrição do parâmetro esperado.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }

                }
                if (arrayTokens[i].replaceAll(",", "").replaceAll(ExpressaoRegular.getTDATATYPE(), "").equals(""))
                {
                    if (!arrayTokens[i - 1].replaceAll(",", "").replaceAll(ExpressaoRegular.getTPARAM(), "").equals(""))
                    {
                        Erro erro = new Erro(2, linhaAtual, "Nome do parâmetro deve vir antes do seu tipo.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }
                    if (i + 1 >= arrayTokens.length)
                    {
                        Erro erro = new Erro(2, linhaAtual, "Descrição do parâmetro esperado.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }

                    if (!arrayTokens[i + 1].replaceAll(",", "").replaceAll(Token.getTSTRING(), "").equals(""))
                    {
                        Erro erro = new Erro(2, linhaAtual, "Descrição do parâmetro esperado.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }
                }
                if (arrayTokens[i].replaceAll(",", "").replaceAll(Token.getTSTRING(), "").equals(""))
                {
                    if (!arrayTokens[i - 1].replaceAll(",", "").replaceAll(ExpressaoRegular.getTDATATYPE(), "").equals(""))
                    {
                        Erro erro = new Erro(2, linhaAtual, "Tipo do parâmetro deve vir antes da sua descrição.");
                        _listaErrosSintaticos.add(erro);
                        return;
                    }
                    if (i + 1 < arrayTokens.length)
                    {
                        if (!arrayTokens[i].endsWith(","))
                        {
                            Erro erro = new Erro(2, linhaAtual, "Caractere de separação faltando ou inválido");
                            _listaErrosSintaticos.add(erro);
                            return;
                        }
                        if (!arrayTokens[i + 1].replace(",", "").replaceAll(ExpressaoRegular.getTPARAM(), "").equals(""))
                        {
                            Erro erro = new Erro(2, linhaAtual, "Parâmetro de método esperado.");
                            _listaErrosSintaticos.add(erro);
                            return;
                        }
                    } else
                    {
                        if (arrayTokens[i].endsWith(","))
                        {
                            Erro erro = new Erro(2, linhaAtual, "Caractere de separação em lugar não válido.");
                            _listaErrosSintaticos.add(erro);
                            return;
                        }
                    }
                }
            }
        }
    }

    private void ReconhecerTDSEE(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTDSEE(), "").equals(""))
        {
            if (arrayTokens.length > 2)
            {
                String mensagemErro = "";

                for (int i = 2; i < arrayTokens.length; i++)
                {
                    mensagemErro += arrayTokens[i] + " ";
                }

                Erro erro = new Erro(2, linhaAtual, "Final de linha esperado mas \"" + mensagemErro.trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }

            if (arrayTokens.length < 2)
            {
                Erro erro = new Erro(2, linhaAtual, "Classe esperada mas final de linha encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
            if (!arrayTokens[1].replaceAll(ExpressaoRegular.getTCLASS(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Classe esperada mas \"" + arrayTokens[1].trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
        }
    }

    private void ReconhecerTDDEPRECATED(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTDDEPRECATED(), "").equals(""))
        {
            if (arrayTokens.length > 2)
            {
                String mensagemErro = "";
                for (int i = 2; i < arrayTokens.length; i++)
                {
                    mensagemErro += arrayTokens[i] + " ";
                }

                Erro erro = new Erro(2, linhaAtual, "Final de linha esperado mas \"" + mensagemErro.trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }

            if (arrayTokens.length < 2)
            {
                Erro erro = new Erro(2, linhaAtual, "Booleano esperada mas final de linha encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
            if (!arrayTokens[1].replaceAll(ExpressaoRegular.getTBOOL(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "Classe esperada mas \"" + arrayTokens[1].trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
        }
    }

    private void ReconhecerTDRETURN(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTDRETURN(), "").equals(""))
        {
            if (arrayTokens.length < 3)
            {
                Erro erro = new Erro(2, linhaAtual, "Final inesperado de linha.");
                _listaErrosSintaticos.add(erro);
                return;
            }
            if (arrayTokens[1].replaceAll(ExpressaoRegular.getTCLASS(), "").equals(""))
            {
                if (!arrayTokens[2].replaceAll(Token.getTSTRING(), "").equals(""))
                {
                    Erro erro = new Erro(2, linhaAtual, "Descrição esperada mas \"" + arrayTokens[2].trim() + "\" encontrado.");
                    _listaErrosSintaticos.add(erro);
                    return;
                }
            } else
            {
                Erro erro = new Erro(2, linhaAtual, "Tipo esperado mas \"" + arrayTokens[1].trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }

            if (arrayTokens.length > 3)
            {
                String mensagemErro = "";
                for (int i = 3; i < arrayTokens.length; i++)
                {
                    mensagemErro += arrayTokens[i] + " ";
                }

                Erro erro = new Erro(2, linhaAtual, "Final de linha esperado mas \"" + mensagemErro.trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
        }
    }

    private void ReconhecerTTFIXME(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTTFIXME(), "").equals(""))
        {
            if (arrayTokens.length > 2)
            {
                String mensagemErro = "";
                for (int i = 2; i < arrayTokens.length; i++)
                {
                    mensagemErro += arrayTokens[i] + " ";
                }

                Erro erro = new Erro(2, linhaAtual, "Final de linha esperado mas \"" + mensagemErro.trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }

            if (arrayTokens.length < 2)
            {
                Erro erro = new Erro(2, linhaAtual, "String esperado mas final de linha encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
            if (!arrayTokens[1].replaceAll(Token.getTSTRING(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "String esperada mas \"" + arrayTokens[1].trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
        }
    }

    private void ReconhecerTTTODO(String[] arrayTokens, int linhaAtual)
    {
        if (arrayTokens[0].replaceAll(ExpressaoRegular.getTTTODO(), "").equals(""))
        {
            if (arrayTokens.length > 2)
            {
                String mensagemErro = "";
                for (int i = 2; i < arrayTokens.length; i++)
                {
                    mensagemErro += arrayTokens[i] + " ";
                }

                Erro erro = new Erro(2, linhaAtual, "Final de linha esperado mas \"" + mensagemErro.trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }

            if (arrayTokens.length < 2)
            {
                Erro erro = new Erro(2, linhaAtual, "String esperado mas final de linha encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
            if (!arrayTokens[1].replaceAll(Token.getTSTRING(), "").equals(""))
            {
                Erro erro = new Erro(2, linhaAtual, "String esperada mas \"" + arrayTokens[1].trim() + "\" encontrado.");
                _listaErrosSintaticos.add(erro);
                return;
            }
        }
    }

    private void ReconhecerTDEFCLASS(String[] arrayTokens, int _linhaAtual)
    {
    }

    private void ReconhecerTDEFMETHOD(String[] arrayTokens, int _linhaAtual)
    {
        //throw new UnsupportedOperationException("Not yet implemented");
    }

    private void ImprimirListaErros()
    {
        if (_listaErrosSintaticos.isEmpty())
        {
            System.out.println("ACEITA");
            return;
        }

        System.out.println("REJEITA");

        Iterator iterator = this._listaErrosSintaticos.iterator();

        while (iterator.hasNext())
        {
            Erro erro = (Erro) iterator.next();

            System.out.println("Erro linha número " + erro.getNumLinha());
            System.out.println("\tCódigo do erro: " + erro.getCodErro());
            System.out.println("\tDescrição: " + erro.getMensagemErro());
            System.out.println("\tMensagem: " + erro.getSequencia());
        }
    }

    public void FecharArquivoTemporario() throws IOException
    {
        _fw.close();
    }

    public void IniciarArquivo() throws IOException
    {
        if (_arquivoTemporario == null)
        {
            _arquivoTemporario = new File("doCommentSintatico.dom");
        }

        if (_fw == null)
        {
            _fw = new FileWriter(_arquivoTemporario);
        }
        if (_bw == null)
        {
            _bw = new BufferedWriter(_fw);
        }
    }

    public static BufferedReader AbrirArquivo(String caminho) throws FileNotFoundException, IOException
    {
        FileReader fr = new FileReader(caminho);

        BufferedReader bf = new BufferedReader(fr);

        return bf;
    }

    public void GravarLinhaCodigoSintatico(ArrayList<String> linha) throws IOException
    {
        Iterator it = linha.iterator();

        while (it.hasNext())
        {
            _fw.write(it.next().toString() + " ");
        }

        _fw.write(System.getProperty("line.separator"));
    }

    private void ConverterArquivoTemporarioListaTokens() throws IOException
    {
        BufferedReader arquivoTemporario = AbrirArquivo("doComment.dom");

        if (arquivoTemporario == null)
        {
            throw new FileNotFoundException("Arquivo temporário não enconrtado.");
        }

        String linha;

        while ((linha = arquivoTemporario.readLine()) != null)
        {
            SubstituirTokensArquivoTemporario(linha);
        }

        _fw.close();
    }

    private void SubstituirTokensArquivoTemporario(String linha) throws IOException
    {
        String linhaDoComment = linha;/*.split("\\d%%")[1].replace("#", "");*/

        linhaDoComment = linhaDoComment.replaceAll(ExpressaoRegular.getTSTRING(), Token.getTSTRING());
        GravarArquivoTemporario(linhaDoComment.trim());
    }

    private void GravarArquivoTemporario(String linha) throws IOException
    {
        _fw.write(linha);
        _fw.write(System.getProperty("line.separator"));
    }
}
